package com.darwin.darwinGpuEngine.core {

    
    import com.darwin.darwinGpuEngine.utils.StatsObject;
    
    import flash.display.Sprite;
    import flash.display3D.Context3D;
    import flash.display3D.Context3DCompareMode;
    import flash.display3D.Context3DTriangleFace;
    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.events.TimerEvent;
    import flash.geom.Rectangle;
    import flash.geom.Vector3D;
    import flash.utils.Timer;
    import flash.utils.getTimer;
    

    /**
     * 初始化完毕并且context3D可用时抛出,isHardwareAccelerated属性在此后才有效
     * @eventType flash.events.Event.INIT
     */
    [Event(name="init", type="flash.events.Event")]

    /**
     * 通过继承此类来添加/删除显示节点
	 * <ul>
	 * <li>通过调用start()方法来启动初始化过程</li>
	 * <li>在各场景/节点的step()方法中实现游戏逻辑</li>
	 * <li>可以通过调用setActiveScene方法来在场景之间切换 <br><b>注:同时只能有一个场景处于激活状态</b></li>
	 * </ul>
	 * @author Roland
     */ 
	public class GWorld extends Sprite {

        protected var camera:GCam = new GCam(1, 1);
        protected var context3D:Context3D;
        protected var stageID:uint;
        protected var scene:GScene;
        protected var frameRate:uint;
        protected var isPaused:Boolean = false;
        protected var bounds:Rectangle;
        protected var lastFramesTime:Number = 0.0;
        protected var enableErrorChecking:Boolean = false;

        protected var renderMode:String;
        protected var mousePosition:Vector3D = new Vector3D(0.0, 0.0, 0.0);
        protected var antialiasing:uint = 2;
        protected var deviceInitialized:Boolean = false;
        protected var deviceWasLost:Boolean = false;
		
		protected var statsObject:StatsObject = new StatsObject();

        protected var initializeNodesAfterStartUp:Boolean = false;

        public static var isHardwareAccelerated:Boolean;

        /**
         * 实例化GWorld
         * @param renderMode 传Context3DRenderMode中的静态常量 (auto, software)
         * @param frameRate 帧率
         * @param bounds 世界的边界,边界外部部分会被截去
         * @param stageID
         */
        public function GWorld(renderMode:String, frameRate:uint, bounds:Rectangle = null, stageID:uint = 0) {

            this.renderMode = renderMode;
            this.frameRate = frameRate;
            this.bounds = bounds;
            this.stageID = stageID;
            addEventListener(Event.ADDED_TO_STAGE, addedToStage);
        }

        protected function addedToStage(event:Event):void {

            removeEventListener(Event.ADDED_TO_STAGE, addedToStage);
            stage.addEventListener(Event.RESIZE, resizeStage);
            stage.frameRate = frameRate;
            stage.stage3Ds[stageID].addEventListener(Event.CONTEXT3D_CREATE, context3DCreated);
            stage.stage3Ds[stageID].addEventListener(ErrorEvent.ERROR, context3DError);
            stage.stage3Ds[stageID].requestContext3D(renderMode);

            stage.addEventListener(MouseEvent.CLICK, mouseEventHandler);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseEventHandler);
            stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseEventHandler);
            stage.addEventListener(MouseEvent.MOUSE_UP, mouseEventHandler);
        }

        protected function context3DError(e:ErrorEvent):void {
            throw new Error("swf没有被正确的嵌入. context3D无法被创建. 检查是否忘记填写 WMODE参数,请尝试将其设置为 'direct'.");
        }

        protected function context3DCreated(e:Event):void {

            context3D = stage.stage3Ds[stageID].context3D;
            context3D.enableErrorChecking = enableErrorChecking;
            context3D.setCulling(Context3DTriangleFace.NONE);
            context3D.setDepthTest(false, Context3DCompareMode.ALWAYS);
            isHardwareAccelerated = context3D.driverInfo.toLowerCase().indexOf("software") == -1;

            resizeStage();

            // 第二次收到Event.CONTEXT3D_CREATE事件,说明上一次没有初始化成功,设备丢失
            if(deviceInitialized) {
                deviceWasLost = true;
            }

            deviceInitialized = true;

            if(initializeNodesAfterStartUp) {
                doInitializeNodes();
            }

            dispatchEvent(new Event(Event.INIT));
        }

        protected function mouseEventHandler(event:MouseEvent):void {
            if(scene && scene.mouseEnabled && stage && camera) {
                var mouseEventType:String = event.type;

                // transformation of normalized coordinates between -1 and 1
                mousePosition.x = (stage.mouseX - 0.0) / camera.sceneWidth * 2.0 - 1.0;
                mousePosition.y = -((stage.mouseY - 0.0) / camera.sceneHeight * 2.0 - 1.0);
                mousePosition.z = 0.0;
                mousePosition.w = 1.0;

                scene.processMouseEvents(mousePosition, mouseEventType, camera.getViewProjectionMatrix());
            }
        }

        protected function resizeStage(e:Event = null):void {
            if(!context3D) return;
            var rect:Rectangle = bounds ? bounds : new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
            stage.stage3Ds[stageID].x = rect.x;
            stage.stage3Ds[stageID].y = rect.y;

            context3D.configureBackBuffer(rect.width, rect.height, antialiasing, false);
            camera.resizeCameraStage(rect.width, rect.height);
        }

        protected function mainLoop(e:Event):void {
            var t:Number = getTimer() / 1000.0;
            var elapsed:Number = t - lastFramesTime;

            if(scene) {
                context3D.clear(scene.br, scene.bg, scene.bb, 1.0);

                if(!isPaused) {
                    scene.timeSinceStartInSeconds = t;
                    scene.stepNode(elapsed);
                }

                if(deviceWasLost) {
                    scene.handleDeviceLoss();
                    deviceWasLost = false;
                }

				statsObject.totalDrawCalls = 0;
				statsObject.totalTris = 0;
				
                scene.drawNode(context3D, camera, false,statsObject);

                context3D.present();
            }

            lastFramesTime = t;
        }

		/**
		 *	激活传入的场景 
		 * @param value
		 * 
		 */
        protected function setActiveScene(value:GScene):void {

            if(scene) {
                scene.setStageRef(null);
                scene.setCameraRef(null);
            }

            this.scene = value;

            if(scene) {
                scene.setCameraRef(camera);
                scene.setStageRef(stage);
            }
        }

        public function start():void {
            wakeUp();
        }

        /**
         * Pause all movement in your game. The drawing loop will still fire
         */
        public function pause():void {
            isPaused = true;
        }

        /**
         * Resume movement in your game.
         */
        public function resume():void {
            isPaused = false;
        }

        /**
         * Put everything to sleep, no drawing and step loop will be fired
         */
        public function sleep():void {
             removeEventListener(Event.ENTER_FRAME, mainLoop);

            if(context3D) {
                context3D.clear(scene.br, scene.bg, scene.bb, 1.0);
                context3D.present();
            }
        }

        /**
         * wake up from sleep. draw / step loops will start to fire again
         */
        public function wakeUp():void {
      		  removeEventListener(Event.ENTER_FRAME, mainLoop);
              addEventListener(Event.ENTER_FRAME, mainLoop);
        }

        /**
         * optionally you can call this method to initialize all your object in the active scene
         * an event will be dispatched when the initializing is done
         */
        public function initializeNodes():void {

            if(deviceInitialized) {
                doInitializeNodes();
            } else {
                initializeNodesAfterStartUp = true;
            }
        }

        private function doInitializeNodes():void {
            // TODO traverse through displaylist and initialize nodes in a seperate thread / loop? dispatch event when ready, etc...
            trace("TODO! Implement initializeNodes");
        }

        public function destroy():void {
            sleep();
            if(context3D) {
                context3D.dispose();
            }
        }
    }
}